package com.xyzboom.compiler.cli

import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.module.kotlin.jsonMapper
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.*
import com.github.ajalt.clikt.parameters.types.file
import com.xyzboom.compiler.compiler.grammar.Grammar2
import com.xyzboom.compiler.compiler.lexer.LexerRule
import com.xyzboom.compiler.compiler.lexer.Token
import com.xyzboom.compiler.compiler.lr.PredictTable
import com.xyzboom.compiler.compiler.lr.generatePredictTable
import com.xyzboom.compiler.util.show
import com.xyzboom.compiler.util.toGraphViz

/**
 * Grammar command
 *
 * @constructor Create empty Grammar command
 */
class GrammarCommand : CliktCommand() {
    private val grammarFile by option(
        "-i", "--input",
        help = "grammar or predict table file input"
    )
        .file()
        .required()
    private val isRaw by option(
        "-r", "--raw",
        help = "raw: input raw grammar file\n" +
                "decoded: input json decoded predict table file"
    )
        .flag("-d", "--decoded", default = true)
    private val tokenFile by option(
        "-it", "--input-token",
        help = "token file input"
    ).file()
    private val predictTableOutFile by option(
        "-op", "--output-predict-table",
        help = "decoded predict table file output path"
    ).file()
    private val astOutFile by option(
        "-oa", "--output-ast",
        help = "decoded ast node file output path"
    ).file()
    private val ignoreTokenNames by option(
        "-ig", "--ignore-token",
        help = "token names to ignore when parse ast"
    ).multiple(required = false).unique()

    /**
     * Run GrammarCommand
     */
    override fun run() {
        val jsonMapper = jsonMapper()
        val tokens: List<Token>? = if (tokenFile != null) {
            jsonMapper.readerFor(object : TypeReference<List<Token>>() {})
                .readValue(
                    tokenFile!!.readText()
                )
        } else {
            null
        }
        val predictTable = if (isRaw) {
            Grammar2.grammarFromText(grammarFile.readText()).generatePredictTable()
        } else {
            jsonMapper.readerFor(PredictTable::class.java).readValue(grammarFile.readText())
        }
        predictTableOutFile?.writeText(jsonMapper.writeValueAsString(predictTable))
        tokens ?: return
        val astNode = predictTable.generatorAST(tokens, ignoreTokenNames)
        astNode.toGraphViz().show()
        astOutFile?.writeText(jsonMapper.writeValueAsString(astNode))
    }
}